﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Virgee.Common.Interfaces;
using Virgee.Common.Serializers;
using Virgee.Common.Services.Exceptions;

namespace Virgee.Common.Services.Clients
{
    /// <summary>
    /// An HTTP client that can easily communicate with a RESTful service. 
    /// </summary>
    public class HttpServiceClient
    {
        #region Fields
        protected ISerializer Serializer;
        #endregion

        #region Constructors
        /// <summary>
        /// Constructs a new instance using the specified <see cref="T:Virgee.Common.Interfaces.ISerializer"/>.
        /// </summary>
        /// <param name="serializer"></param>
        public HttpServiceClient(ISerializer serializer)
        {
            Is.NotNull(serializer, "serializer");

            Serializer = serializer;
        }
        #endregion // Constructors

        #region Public Methods

        #region GET

        /// <summary>
        /// Executes a synchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <returns>A new instance of type  <typeparamref name="T"/>.</returns>
        public T Get<T>(string resourceUriString, object resourceId) where T : class
        {
            Is.NotNull(resourceId, "resourceId");
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");

            return DoGetAsync<T>(BuildURI(resourceUriString, resourceId), CancellationToken.None).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>A new instance of type  <typeparamref name="T"/>.</returns>
        public T Get<T>(string resourceUriString, object resourceId, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceId, "resourceId");
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");

            return DoGetAsync<T>(BuildURI(resourceUriString, resourceId), cancellationToken).Result;
        }

        /// <summary>
        /// Executes an asynchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> GetAsync<T>(string resourceUriString, object resourceId) where T : class
        {
            Is.NotNull(resourceId, "resourceId");
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");

            return await DoGetAsync<T>(BuildURI(resourceUriString, resourceId), CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> GetAsync<T>(string resourceUriString, object resourceId, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceId, "resourceId");
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");

            return await DoGetAsync<T>(BuildURI(resourceUriString, resourceId), cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="query">A key/value collection used to query the service.</param>
        /// <returns>A new instance of the specified generic type.</returns>
        public T Get<T>(string resourceUriString, IEnumerable<KeyValuePair<string, string>> query) where T : class
        {
            Is.NotNull(query, "query");
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");

            return DoGetAsync<T>(BuildURI(resourceUriString, query), CancellationToken.None).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="query">A key/value collection used to query the service.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>A new instance of the specified generic type.</returns>
        public T Get<T>(string resourceUriString, IEnumerable<KeyValuePair<string, string>> query, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(query, "query");
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");

            return DoGetAsync<T>(BuildURI(resourceUriString, query), cancellationToken).Result;
        }

        /// <summary>
        /// Executes an asynchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="query">A key/value collection used to query the service.</param>
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> GetAsync<T>(string resourceUriString, IEnumerable<KeyValuePair<string, string>> query) where T : class
        {
            Is.NotNull(query, "query");
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");

            return await DoGetAsync<T>(BuildURI(resourceUriString, query), CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="query">A key/value collection used to query the service.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> GetAsync<T>(string resourceUriString, IEnumerable<KeyValuePair<string, string>> query, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(query, "query");
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");

            return await DoGetAsync<T>(BuildURI(resourceUriString, query), cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <returns>A new instance of the specified generic type.</returns>
        public T Get<T>(Uri resourceUri) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            return DoGetAsync<T>(resourceUri, CancellationToken.None).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>A new instance of the specified generic type.</returns>
        public T Get<T>(Uri resourceUri, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            return DoGetAsync<T>(resourceUri, cancellationToken).Result;
        }

        /// <summary>
        /// Executes an asynchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> GetAsync<T>(Uri resourceUri) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            return await DoGetAsync<T>(resourceUri, CancellationToken.None);
        }
        
        /// <summary>
        /// Executes an asynchronous HTTP GET request to retrieve the specified resource.
        /// The resource should be de-serializable to the specified generic type.
        /// </summary>
        /// <typeparam name="T">The type of the resource.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> GetAsync<T>(Uri resourceUri, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            return await DoGetAsync<T>(resourceUri, cancellationToken);
        }
        
        #endregion // GET

        #region POST

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post.
        /// </summary>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task PostAsync(string resourceUriString, object objectToPost)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(objectToPost, "objectToPost");

            UriBuilder uriBuilder = new UriBuilder(resourceUriString);
            await DoPostAsync(uriBuilder.Uri, objectToPost, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post.
        /// </summary>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task PostAsync(string resourceUriString, object objectToPost, CancellationToken cancellationToken)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(objectToPost, "objectToPost");

            UriBuilder uriBuilder = new UriBuilder(resourceUriString);
            await DoPostAsync(uriBuilder.Uri, objectToPost, cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post.
        /// </summary>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="objectToPost">The object to post.</param>
        public void Post(string resourceUriString, object objectToPost)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(objectToPost, "objectToPost");

            UriBuilder uriBuilder = new UriBuilder(resourceUriString);
            DoPostAsync(uriBuilder.Uri, objectToPost, CancellationToken.None).Wait();
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post.
        /// </summary>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        public void Post(string resourceUriString, object objectToPost, CancellationToken cancellationToken)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(objectToPost, "objectToPost");

            UriBuilder uriBuilder = new UriBuilder(resourceUriString);
            DoPostAsync(uriBuilder.Uri, objectToPost, cancellationToken).Wait();
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post.
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task PostAsync(Uri resourceUri, object objectToPost)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            await DoPostAsync(resourceUri, objectToPost, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post.
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task PostAsync(Uri resourceUri, object objectToPost, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            await DoPostAsync(resourceUri, objectToPost, cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post.
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="objectToPost">The object to post.</param>
        public void Post(Uri resourceUri, object objectToPost)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            DoPostAsync(resourceUri, objectToPost, CancellationToken.None).Wait();
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post.
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        public void Post(Uri resourceUri, object objectToPost, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            DoPostAsync(resourceUri, objectToPost, cancellationToken).Wait();
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI string.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> PostAsync<T>(string resourceUriString, object objectToPost) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(objectToPost, "objectToPost");

            UriBuilder uriBuilder = new UriBuilder(resourceUriString);
            return await DoPostAsync<T>(uriBuilder.Uri, objectToPost, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI string.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> PostAsync<T>(string resourceUriString, object objectToPost, CancellationToken cancellationToken) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(objectToPost, "objectToPost");

            UriBuilder uriBuilder = new UriBuilder(resourceUriString);
            return await DoPostAsync<T>(uriBuilder.Uri, objectToPost, cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI string.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <returns>The response object.</returns>
        public T Post<T>(string resourceUriString, object objectToPost) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(objectToPost, "objectToPost");

            UriBuilder uriBuilder = new UriBuilder(resourceUriString);
            return DoPostAsync<T>(uriBuilder.Uri, objectToPost, CancellationToken.None).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI string.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>The response object.</returns>
        public T Post<T>(string resourceUriString, object objectToPost, CancellationToken cancellationToken) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(objectToPost, "objectToPost");

            UriBuilder uriBuilder = new UriBuilder(resourceUriString);
            return DoPostAsync<T>(uriBuilder.Uri, objectToPost, cancellationToken).Result;
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> PostAsync<T>(Uri resourceUri, object objectToPost) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            return await DoPostAsync<T>(resourceUri, objectToPost, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>A task with a result of the specified generic type.</returns>        
        public async Task<T> PostAsync<T>(Uri resourceUri, object objectToPost, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            return await DoPostAsync<T>(resourceUri, objectToPost, cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <returns>The response object.</returns>
        public T Post<T>(Uri resourceUri, object objectToPost) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            return DoPostAsync<T>(resourceUri, objectToPost, CancellationToken.None).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified object 
        /// to the given resource URI using a raw post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="objectToPost">The object to post.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>The response object.</returns>
        public T Post<T>(Uri resourceUri, object objectToPost, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            return DoPostAsync<T>(resourceUri, objectToPost, cancellationToken).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified data and files 
        /// to the given resource URI using a form-data post. 
        /// </summary>        
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="formData">The list of key/value pairs.</param>
        /// <param name="files">The list of file names </param>                
        /// <remarks>When posting files, use full path file names.</remarks>
        public void Post(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files) 
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            DoPostAsync(resourceUri, formData, files, CancellationToken.None).Wait();
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified data and files 
        /// to the given resource URI using a form-data post. 
        /// </summary>        
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="formData">The list of key/value pairs.</param>
        /// <param name="files">The list of file names </param>                
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <remarks>When posting files, use full path file names.</remarks>
        public void Post(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            DoPostAsync(resourceUri, formData, files, cancellationToken).Wait();
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified data and files 
        /// to the given resource URI using a form-data post. 
        /// </summary>        
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="formData">The list of key/value pairs.</param>
        /// <param name="files">The list of file names </param>        
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        /// <remarks>When posting files, use full path file names.</remarks>
        public async Task PostAsync(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files) 
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            await DoPostAsync(resourceUri, formData, files, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified data and files 
        /// to the given resource URI using a form-data post. 
        /// </summary>        
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="formData">The list of key/value pairs.</param>
        /// <param name="files">The list of file names </param>        
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        // <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        /// <remarks>When posting files, use full path file names.</remarks>
        public async Task PostAsync(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            await DoPostAsync(resourceUri, formData, files, cancellationToken);
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified data and files 
        /// to the given resource URI using a form-data post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="formData">The list of key/value pairs.</param>
        /// <param name="files">The list of file names </param>        
        /// <returns>A task with a result of the specified generic type.</returns>
        /// <remarks>When posting files, use full path file names.</remarks>
        public async Task<T> PostAsync<T>(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            return await DoPostAsync<T>(resourceUri, formData, files, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP POST request to post the specified data and files 
        /// to the given resource URI using a form-data post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="formData">The list of key/value pairs.</param>
        /// <param name="files">The list of file names </param>        
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>
        /// <returns>A task with a result of the specified generic type.</returns>
        /// <remarks>When posting files, use full path file names.</remarks>
        public async Task<T> PostAsync<T>(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            return await DoPostAsync<T>(resourceUri, formData, files, cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified data and files 
        /// to the given resource URI using a form-data post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="formData">The list of key/value pairs.</param>
        /// <param name="files">The list of file names </param>        
        /// <returns>The response object.</returns>
        /// <remarks>When posting files, use full path file names.</remarks>
        public T Post<T>(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            return DoPostAsync<T>(resourceUri, formData, files, CancellationToken.None).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP POST request to post the specified data and files 
        /// to the given resource URI using a form-data post. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="formData">The list of key/value pairs.</param>
        /// <param name="files">The list of file names.</param>        
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>
        /// <returns>The response object.</returns>
        /// <remarks>When posting files, use full path file names.</remarks>
        public T Post<T>(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            return DoPostAsync<T>(resourceUri, formData, files, cancellationToken).Result;
        }
        #endregion // POST

        #region PUT
                
        /// <summary>
        /// Executes an asynchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. 
        /// </summary>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="objectToPut">The object to put.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task PutAsync(string resourceUriString, object resourceId, object objectToPut)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");
            Is.NotNull(objectToPut, "objectToPut");
            
            await DoPutAsync(BuildURI(resourceUriString, resourceId), objectToPut, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. 
        /// </summary>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="objectToPut">The object to put.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task PutAsync(string resourceUriString, object resourceId, object objectToPut, CancellationToken cancellationToken)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");
            Is.NotNull(objectToPut, "objectToPut");

            await DoPutAsync(BuildURI(resourceUriString, resourceId), objectToPut, cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. 
        /// </summary>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="objectToPut">The object to put.</param>        
        public void Put(string resourceUriString, object resourceId, object objectToPut)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");
            Is.NotNull(objectToPut, "objectToPut");

            DoPutAsync(BuildURI(resourceUriString, resourceId), objectToPut, CancellationToken.None).Wait();
        }

        /// <summary>
        /// Executes a synchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. 
        /// </summary>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="objectToPut">The object to put.</param>        
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        public void Put(string resourceUriString, object resourceId, object objectToPut, CancellationToken cancellationToken)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");
            Is.NotNull(objectToPut, "objectToPut");

            DoPutAsync(BuildURI(resourceUriString, resourceId), objectToPut, cancellationToken).Wait();
        }

        /// <summary>
        /// Executes an asynchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. 
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>        
        /// <param name="objectToPut">The object to put.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task PutAsync(Uri resourceUri, object objectToPut)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            await DoPutAsync(resourceUri, objectToPut, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. 
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>        
        /// <param name="objectToPut">The object to put.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task PutAsync(Uri resourceUri, object objectToPut, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            await DoPutAsync(resourceUri, objectToPut, cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. 
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>        
        /// <param name="objectToPut">The object to put.</param>    
        public void Put(Uri resourceUri, object objectToPut)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            DoPutAsync(resourceUri, objectToPut, CancellationToken.None).Wait();
        }

        /// <summary>
        /// Executes a synchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. 
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>        
        /// <param name="objectToPut">The object to put.</param>    
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        public void Put(Uri resourceUri, object objectToPut, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            DoPutAsync(resourceUri, objectToPut, cancellationToken).Wait();
        }

        /// <summary>
        /// Executes an asynchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="objectToPut">The object to put.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing 
        /// the request with a result of the specified generic type.</returns>
        public async Task<T> PutAsync<T>(string resourceUriString, object resourceId, object objectToPut) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");
            Is.NotNull(objectToPut, "objectToPut");

            return await DoPutAsync<T>(BuildURI(resourceUriString, resourceId), objectToPut, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="objectToPut">The object to put.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing 
        /// the request with a result of the specified generic type.</returns>
        public async Task<T> PutAsync<T>(string resourceUriString, object resourceId, object objectToPut, CancellationToken cancellationToken) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");
            Is.NotNull(objectToPut, "objectToPut");

            return await DoPutAsync<T>(BuildURI(resourceUriString, resourceId), objectToPut, cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="objectToPut">The object to put.</param>     
        /// <returns>The response object.</returns>
        public T Put<T>(string resourceUriString, object resourceId, object objectToPut) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");
            Is.NotNull(objectToPut, "objectToPut");
            
            return DoPutAsync<T>(BuildURI(resourceUriString, resourceId), objectToPut, CancellationToken.None).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUriString">The resource URI string.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="objectToPut">The object to put.</param>     
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>The response object.</returns>
        public T Put<T>(string resourceUriString, object resourceId, object objectToPut, CancellationToken cancellationToken) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");
            Is.NotNull(objectToPut, "objectToPut");

            return DoPutAsync<T>(BuildURI(resourceUriString, resourceId), objectToPut, cancellationToken).Result;
        }

        /// <summary>
        /// Executes an asynchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>        
        /// <param name="objectToPut">The object to put.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing 
        /// the request with a result of the specified generic type.</returns>
        public async Task<T> PutAsync<T>(Uri resourceUri, object objectToPut) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            return await DoPutAsync<T>(resourceUri, objectToPut, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>        
        /// <param name="objectToPut">The object to put.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing 
        /// the request with a result of the specified generic type.</returns>
        public async Task<T> PutAsync<T>(Uri resourceUri, object objectToPut, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            return await DoPutAsync<T>(resourceUri, objectToPut, cancellationToken);
        }

        /// <summary>
        /// Executes a synchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>        
        /// <param name="objectToPut">The object to put.</param>    
        /// <returns>The response object.</returns>
        public T Put<T>(Uri resourceUri, object objectToPut) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            return DoPutAsync<T>(resourceUri, objectToPut, CancellationToken.None).Result;
        }
        
        /// <summary>
        /// Executes a synchronous HTTP PUT request to updated the specified resource id 
        /// with the given object. A body response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>        
        /// <param name="objectToPut">The object to put.</param>    
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>The response object.</returns>
        public T Put<T>(Uri resourceUri, object objectToPut, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            return DoPutAsync<T>(resourceUri, objectToPut, cancellationToken).Result;
        }
        #endregion // PUT

        #region DELETE
        /// <summary>
        /// Executes a synchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>
        public void Delete(Uri resourceUri)
        {
            Is.NotNull(resourceUri, "resourceUri");

            DoDeleteAsync(resourceUri, CancellationToken.None).Wait();
        }

        /// <summary>
        /// Executes a synchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>
        public void Delete(Uri resourceUri, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");

            DoDeleteAsync(resourceUri, cancellationToken).Wait();
        }

        /// <summary>
        /// Executes a synchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        public void Delete(string resourceUriString, object resourceId)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");

            DoDeleteAsync(BuildURI(resourceUriString, resourceId), CancellationToken.None).Wait();
        }

        /// <summary>
        /// Executes a synchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>
        public void Delete(string resourceUriString, object resourceId, CancellationToken cancellationToken)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");

            DoDeleteAsync(BuildURI(resourceUriString, resourceId), cancellationToken).Wait();
        }

        /// <summary>
        /// Executes a synchronous HTTP DELETE request to deleted a resource. A body 
        /// response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <returns></returns>
        public T Delete<T>(Uri resourceUri) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            return DoDeleteAsync<T>(resourceUri, CancellationToken.None).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP DELETE request to deleted a resource. A body 
        /// response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>
        /// <returns></returns>
        public T Delete<T>(Uri resourceUri, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            return DoDeleteAsync<T>(resourceUri, cancellationToken).Result;
        }
        

        /// <summary>
        /// Executes a synchronous HTTP DELETE request to deleted a resource. A body 
        /// response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <returns></returns>
        public T Delete<T>(string resourceUriString, object resourceId) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");

            return DoDeleteAsync<T>(BuildURI(resourceUriString, resourceId), CancellationToken.None).Result;
        }

        /// <summary>
        /// Executes a synchronous HTTP DELETE request to deleted a resource. A body 
        /// response of the specified type is expected.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>
        /// <returns></returns>
        public T Delete<T>(string resourceUriString, object resourceId, CancellationToken cancellationToken) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");

            return DoDeleteAsync<T>(BuildURI(resourceUriString, resourceId), cancellationToken).Result;
        }        

        /// <summary>
        /// Executes an asynchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task DeleteAsync(Uri resourceUri)
        {
            Is.NotNull(resourceUri, "resourceUri");

            await DoDeleteAsync(resourceUri, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task DeleteAsync(Uri resourceUri, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");

            await DoDeleteAsync(resourceUri, cancellationToken);
        }        

        /// <summary>
        /// Executes an asynchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task DeleteAsync(string resourceUriString, object resourceId)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");

            await DoDeleteAsync(BuildURI(resourceUriString, resourceId), CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing the request.</returns>
        public async Task DeleteAsync(string resourceUriString, object resourceId, CancellationToken cancellationToken)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");

            await DoDeleteAsync(BuildURI(resourceUriString, resourceId), cancellationToken);
        }

        /// <summary>
        /// Executes an asynchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing 
        /// the request with a result of the specified generic type.</returns>
        public async Task<T> DeleteAsync<T>(Uri resourceUri) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            return await DoDeleteAsync<T>(resourceUri, CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUri">The resource URI.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing 
        /// the request with a result of the specified generic type.</returns>
        public async Task<T> DeleteAsync<T>(Uri resourceUri, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            return await DoDeleteAsync<T>(resourceUri, cancellationToken);
        }

        /// <summary>
        /// Executes an asynchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing 
        /// the request with a result of the specified generic type.</returns>
        public async Task<T> DeleteAsync<T>(string resourceUriString, object resourceId) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");

            return await DoDeleteAsync<T>(BuildURI(resourceUriString, resourceId), CancellationToken.None);
        }

        /// <summary>
        /// Executes an asynchronous HTTP DELETE request to deleted a resource.
        /// </summary>
        /// <typeparam name="T">The type of the response object.</typeparam>
        /// <param name="resourceUriString">The resource URI.</param>
        /// <param name="resourceId">The resource ID.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the request.</param>        
        /// <returns>An instance of a <see cref="T:System.Thread.Tasks.Task"/> executing 
        /// the request with a result of the specified generic type.</returns>
        public async Task<T> DeleteAsync<T>(string resourceUriString, object resourceId, CancellationToken cancellationToken) where T : class
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");

            return await DoDeleteAsync<T>(BuildURI(resourceUriString, resourceId), cancellationToken);
        }
        #endregion // DELETE

        #endregion // Public Methods

        #region Private Methods

        private Uri BuildURI(string resourceUriString, object resourceId)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(resourceId, "resourceId");

            UriBuilder uriBuilder = new UriBuilder(resourceUriString + resourceId.ToString());
            return uriBuilder.Uri;
        }

        private Uri BuildURI(string resourceUriString, IEnumerable<KeyValuePair<string, string>> query)
        {
            Is.NotNullOrWhiteSpace(resourceUriString, "resourceUriString");
            Is.NotNull(query, "query");

            string queryString = string.Empty;

            if (query.Any())
            {
                queryString = string.Join("&",
                    query.Select(
                        q => string.Format("{0}={1}",
                            q.Key, HttpUtility.HtmlEncode(q.Value)
                        )
                    )
                );
               
            }

            UriBuilder uriBuilder = new UriBuilder(resourceUriString) { Query = queryString };
            return uriBuilder.Uri;
        }

        private async Task<T> DoGetAsync<T>(Uri resourceUri, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            using (HttpClient httpClient = new HttpClient())
            {
                using (HttpResponseMessage responseMessage = await httpClient.GetAsync(resourceUri, cancellationToken))
                {
                    string responseContent = await responseMessage.Content.ReadAsStringAsync();
                    if (responseMessage.IsSuccessStatusCode)
                    {
                        return Serializer.Deserialize<T>(responseContent);
                    }

                    throw new HttpRequestFailedException((int)responseMessage.StatusCode, responseMessage.ReasonPhrase, responseContent);
                }
            }
        }

        private async Task DoPostAsync(Uri resourceUri, object objectToPost, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            using (HttpClient httpClient = new HttpClient())
            {
                using (var content = new StringContent(Serializer.Serialize(objectToPost), Encoding.UTF8, Serializer.MediaType))
                {
                    using (HttpResponseMessage responseMessage = await httpClient.PostAsync(resourceUri, content, cancellationToken))
                    {
                        responseMessage.EnsureSuccessStatusCode();
                    }
                }
            }
        }

        private async Task<T> DoPostAsync<T>(Uri resourceUri, object objectToPost, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPost, "objectToPost");

            using (HttpClient httpClient = new HttpClient())
            {
                using (var content = new StringContent(Serializer.Serialize(objectToPost), Encoding.UTF8, Serializer.MediaType))
                {
                    using (HttpResponseMessage responseMessage = await httpClient.PostAsync(resourceUri, content, cancellationToken))
                    {
                        string responseContent = await responseMessage.Content.ReadAsStringAsync();
                        if (responseMessage.IsSuccessStatusCode)
                        {
                            return Serializer.Deserialize<T>(responseContent);
                        }

                        throw new HttpRequestFailedException((int)responseMessage.StatusCode, responseMessage.ReasonPhrase, responseContent);
                    }
                }
            }
        }

        private async Task DoPostAsync(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            using (HttpClient httpClient = new HttpClient())
            {
                using (var content = new MultipartFormDataContent())
                {
                    if (formData != null)
                    {
                        foreach (var keyValuePair in formData)
                        {
                            content.Add(new StringContent(keyValuePair.Value), keyValuePair.Key);
                        }
                    }

                    if (files != null)
                    {
                        foreach (var file in files)
                        {
                            var fileContent = new ByteArrayContent(System.IO.File.ReadAllBytes(file));
                            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                            {
                                FileName = Path.GetFileName(file)
                            };

                            content.Add(fileContent);
                        }
                    }

                    using (HttpResponseMessage responseMessage = await httpClient.PostAsync(resourceUri, content, cancellationToken))
                    {
                        responseMessage.EnsureSuccessStatusCode();
                    }
                }
            }
        }

        private async Task<T> DoPostAsync<T>(Uri resourceUri, IEnumerable<KeyValuePair<string, string>> formData, IEnumerable<string> files, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.AtLeastOneNotNull(formData, files);

            using (HttpClient httpClient = new HttpClient())
            {
                using (var content = new MultipartFormDataContent())
                {
                    if (formData != null)
                    {
                        foreach (var keyValuePair in formData)
                        {
                            content.Add(new StringContent(keyValuePair.Value), keyValuePair.Key);
                        }
                    }

                    if (files != null)
                    {
                        foreach (var file in files)
                        {
                            var fileContent = new ByteArrayContent(System.IO.File.ReadAllBytes(file));
                            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                            {
                                FileName = Path.GetFileName(file)
                            };

                            content.Add(fileContent);
                        }
                    }

                    using (HttpResponseMessage responseMessage = await httpClient.PostAsync(resourceUri, content, cancellationToken))
                    {
                        string responseContent = await responseMessage.Content.ReadAsStringAsync();
                        if (responseMessage.IsSuccessStatusCode)
                        {
                            return Serializer.Deserialize<T>(responseContent);
                        }

                        throw new HttpRequestFailedException((int)responseMessage.StatusCode, responseMessage.ReasonPhrase, responseContent);
                    }
                }
            }
        }

        private async Task DoPutAsync(Uri resourceUri, object objectToPut, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            using (HttpClient httpClient = new HttpClient())
            {
                using (var content = new StringContent(Serializer.Serialize(objectToPut), Encoding.UTF8, Serializer.MediaType))
                {
                    using (HttpResponseMessage responseMessage = await httpClient.PutAsync(resourceUri, content, cancellationToken))
                    {
                        responseMessage.EnsureSuccessStatusCode();
                    }
                }
            }
        }

        private async Task<T> DoPutAsync<T>(Uri resourceUri, object objectToPut, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");
            Is.NotNull(objectToPut, "objectToPut");

            using (HttpClient httpClient = new HttpClient())
            {
                using (var content = new StringContent(Serializer.Serialize(objectToPut), Encoding.UTF8, Serializer.MediaType))
                {
                    using (HttpResponseMessage responseMessage = await httpClient.PutAsync(resourceUri, content, cancellationToken))
                    {
                        string responseContent = await responseMessage.Content.ReadAsStringAsync();
                        if (responseMessage.IsSuccessStatusCode)
                        {
                            return Serializer.Deserialize<T>(responseContent);
                        }

                        throw new HttpRequestFailedException((int)responseMessage.StatusCode, responseMessage.ReasonPhrase, responseContent);
                    }
                }
            }
        }

        private async Task DoDeleteAsync(Uri resourceUri, CancellationToken cancellationToken)
        {
            Is.NotNull(resourceUri, "resourceUri");
            using (HttpClient httpClient = new HttpClient())
            {
                using (HttpResponseMessage responseMessage = await httpClient.DeleteAsync(resourceUri, cancellationToken))
                {
                    responseMessage.EnsureSuccessStatusCode();
                }
            }
        }

        private async Task<T> DoDeleteAsync<T>(Uri resourceUri, CancellationToken cancellationToken) where T : class
        {
            Is.NotNull(resourceUri, "resourceUri");

            using (HttpClient httpClient = new HttpClient())
            {
                using (HttpResponseMessage responseMessage = await httpClient.DeleteAsync(resourceUri, cancellationToken))
                {
                    string responseContent = await responseMessage.Content.ReadAsStringAsync();
                    if (responseMessage.IsSuccessStatusCode)
                    {
                        return Serializer.Deserialize<T>(responseContent);
                    }

                    throw new HttpRequestFailedException((int)responseMessage.StatusCode, responseMessage.ReasonPhrase, responseContent);
                }
            }
        }

        #endregion // Private Methods
    }
}
